package modeler.shape;


/**
 * @author ags
 */
public class Cylinder extends Shape {

  private static final long serialVersionUID = 3256437006337718072L;
  
  /**
   * Required for IO
   */
  public Cylinder() {}
  
  /**
   * @see modeler.shape.Shape#buildMesh()
   */
  public void buildMesh() {

    int slices = (int) Math.ceil(Math.PI / (8 * TOLERANCE));
    int segments = (int) Math.ceil(Math.PI / (4 * TOLERANCE));
   
    // Create all the vertex data
    int size = 2 * (segments + 1) + 2 + (slices + 1) * (segments + 1);
    float[] vertices = new float[3*size];
    float[] normals = new float[3*size];

    //Create the pole vertices
    int pos = 0;
    vertices[3*pos] = 0;
    vertices[3*pos+1] = 1;
    vertices[3*pos+2] = 0;
    normals[3*pos] = 0;
    normals[3*pos+1] = 1;
    normals[3*pos+2] = 0;
    pos++;
    vertices[3*pos] = 0;
    vertices[3*pos+1] = -1;
    vertices[3*pos+2] = 0;
    normals[3*pos] = 0;
    normals[3*pos+1] = -1;
    normals[3*pos+2] = 0;
    pos++;
	
    // Create the cap vertices must duplicate the edges to get sharp corner
    for (int ctr = 0; ctr <= segments; ctr++) {      
	      vertices[3*pos] = cos(ctr * 2 * Math.PI / segments);
	      vertices[3*pos+1] = 1;
	      vertices[3*pos+2] = sin(ctr * 2 * Math.PI / segments);
	      normals[3*pos] = 0;
	      normals[3*pos+1] = 1;
	      normals[3*pos+2] = 0;
	      pos++;
	    }
	    
    for (int ctr = 0; ctr <= segments; ctr++) {
	      vertices[3*pos] = cos(ctr * 2 * Math.PI / segments);
	      vertices[3*pos+1] = -1;
	      vertices[3*pos+2] = sin(ctr * 2 * Math.PI / segments);
	      normals[3*pos] = 0;
	      normals[3*pos+1] = -1;
	      normals[3*pos+2] = 0;
	      pos++;
	    }

    // Create the intermediate vertices
	for (int ctr1 = 0; ctr1 <= slices; ctr1++) {

      // The height of the current row
      float h = 1 - 2.0f * ctr1 / slices;

      for (int ctr2 = 0; ctr2 <= segments; ctr2++) {

        // The cosine and sine of the zenith
        float cAcross1 = cos(ctr2 * 2 * Math.PI / segments);
        float sAcross1 = sin(ctr2 * 2 * Math.PI / segments);

        // Create the vertices
        vertices[3*pos] = cAcross1;
	    vertices[3*pos+1] = h;
        vertices[3*pos+2] = sAcross1;
	        
        normals[3*pos] = cAcross1;
        normals[3*pos+1] = 0;
        normals[3*pos+2] = sAcross1;
        pos++;
	      }
    }

    // Create the triangles

    // Top
    int tris = 0;
    int nextV;
    size = 2 * segments + 2 * slices * segments;
    int[] triangles = new int[3*size];
    for (int i = 0; i < segments; i++) {
      nextV = i + 1;
      triangles[3*tris] = 0;
      triangles[3*tris+2] = i+2;
      triangles[3*tris+1] = nextV + 2;
      tris++;
    }
    
    // Bottom
    int bRowOffset = 2 + segments + 1;
    for (int i = 0; i < segments; i++) {
      nextV = i + 1;
      triangles[3*tris] = 1;
      triangles[3*tris+1] = i + bRowOffset;
      triangles[3*tris+2] = nextV + bRowOffset;
      tris++;
    }

    // Middle
    int topL, topR, botL, botR;
    for (int j = 0; j < slices; j++) {
      for (int i = 0; i < segments; i++) {
	        topL = (j + 2) * (segments + 1) + i + 2;
	        topR = topL + 1;
	        botL = (j + 3) * (segments + 1) + i + 2;
	        botR = botL + 1;

	        triangles[3*tris] = topL;
	        triangles[3*tris+1] = topR;
	        triangles[3*tris+2] = botL;
	        tris++;
	        triangles[3*tris] = botL;
	        triangles[3*tris+1] = topR;
	        triangles[3*tris+2] = botR;
	        tris++;

      	}
      }
	    
	    mesh = new Mesh(vertices, triangles, normals, 100);
  }
}
